fatfs文件系统详解之f

您所在的位置:网站首页 fopen_s 返回值13 fatfs文件系统详解之f

fatfs文件系统详解之f

2023-08-14 02:00| 来源: 网络整理| 查看: 265

前序

前面分析了f_mkfs、f_mount函数,在格式化和挂载完成之后,当然是要创建文件的操作,本节分析f_open函数,分析一下创建和打开一个文件时,发生了什么。

分析假设

(1)假设一个磁盘就一个分区。

(2)只分析FAT32文件系统相关的代码。

(3)函数的大部分分析,都写入代码注释中。

(4)重要的注释都回加入很多星号以及数学标号。例如, /****************** 1.把字符存入lfn的buffer中 *******************/

关键结构体

(1)FAT文件系统描述一个文件夹的结构体。虽然是一个文件夹结构体,但是再打开一个文件的开始,先创建这个结构体,然后先把文件的信息填充到这个结构体,最终使用这个结构体填充FIL结构图的一些重要信息。

typedef struct { _FDID obj; /* 存放下一个目标的信息 */ DWORD dptr; /* 当前目录项的起始地址(单位:字节) */ DWORD clust; /* 当前簇 */ DWORD sect; /* 当前簇对应的扇区位置(通过目录项的位置确定) */ BYTE* dir; /* 指向当前扇区win[]中的偏移(单位:字节) */ BYTE fn[12]; /* 存放8.3文件名,最后1个字节存放标志:是否有长目录项 */ #if _USE_LFN != 0 DWORD blk_ofs; /* 指向第1个长目录项的起始位置(字节:单位) (0xFFFFFFFF:Invalid) */ #endif #if _USE_FIND const TCHAR* pat; /* Pointer to the name matching pattern */ #endif } DIR;

(2)FAT文件系统描述一个文件的结构图。

typedef struct { _FDID obj; /* 存放下一个目标的信息 */ BYTE flag; /* 文件状态标志 */ BYTE err; /* 文件错误码 */ FSIZE_t fptr; /* 文件的读写指针位置 */ DWORD clust; /* fptr所在的当前簇 */ DWORD sect; /* Sector number appearing in buf[] (0:invalid) */ #if !_FS_READONLY DWORD dir_sect; /* Sector number containing the directory entry */ BYTE* dir_ptr; /* Pointer to the directory entry in the win[] */ #endif #if _USE_FASTSEEK DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */ #endif #if !_FS_TINY BYTE buf[_MAX_SS]; /* File private data read/write window */ #endif } FIL;

(3)_FDID结构体。 在(1)和(2)的结构体中,都有_FDID结构体,那么分析一下这个结构体的内容: 注:在FATFS中有个全局的指针数组FATFS[],在f_mount的时候可知,这个数组会被填充,填充的内容是MBR的内容,之后,我们把这个数组指针称作为**“超级快”**。

typedef struct { FATFS* fs; /* 指向全局的在挂载的时候磁盘对应的超级块 */ WORD id; /* Owner file system mount ID */ BYTE attr; /* 目标属性,根据目录项中的属性进行填充 */ BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous (no data on FAT), =3:got flagmented, b2:sub-directory stretched) */ DWORD sclust; /* 目标的起始簇号 */ FSIZE_t objsize; /* 文件或者文件夹的大小 */ #if _FS_LOCK != 0 UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */ #endif } _FDID;

f_open函数分析 下面是f_open的源代码:

FRESULT f_open ( FIL* fp, /* 返回的文件描述符 */ const TCHAR* path, /* 打开文件的路径 */ BYTE mode /* 文件打开的模式 */ ) { FRESULT res; DIR dj; /* 分配一个DIR结构体 */ FATFS *fs; #if !_FS_READONLY DWORD dw, cl, bcs, clst, sc; FSIZE_t ofs; #endif if (!fp) return FR_INVALID_OBJECT; /* Get logical drive */ mode &= _FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND | FA_SEEKEND; res = find_volume(&path, &fs, mode); if (res == FR_OK) { dj.obj.fs = fs; /* 根据不同的配置来分配fs->lfn,用于存放path指向的路径 */ INIT_NAMBUF(fs); res = follow_path(&dj, path); /* Follow the file path */ #if !_FS_READONLY /* R/W configuration */ if (res == FR_OK) { if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory itself? */ res = FR_INVALID_NAME; } #if _FS_LOCK != 0 else { res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0); } #endif } /* Create or Open a file */ if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { if (res != FR_OK) { /* 文件不存在 创建文件,省略不少代码 */ } else { /* 文件存在 但是不允许写入 */ if (dj.obj.attr & (AM_RDO | AM_DIR)) { /* Cannot overwrite it (R/O or DIR) */ res = FR_DENIED; } else { if (mode & FA_CREATE_NEW) res = FR_EXIST; /* Cannot create as new file */ } } if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* 覆盖文件,省略不少代码 */ } } else { /* 成功打开一个文件 */ if (res == FR_OK) { /* Following succeeded */ if (dj.obj.attr & AM_DIR) { /* It is a directory */ res = FR_NO_FILE; } else { if ((mode & FA_WRITE) && (dj.obj.attr & AM_RDO)) { /* R/O violation */ res = FR_DENIED; } } } } if (res == FR_OK) { if (mode & FA_CREATE_ALWAYS) /* Set file change flag if created or overwritten */ mode |= FA_MODIFIED; fp->dir_sect = fs->winsect; /* Pointer to the directory entry */ fp->dir_ptr = dj.dir; } #else #endif if (res == FR_OK) { /* 使用dj结构体对文件描述符fp填充 */ /* 具体填充的代码,参考源码,最终会进行一些总结概括 */ } FREE_NAMBUF(); } if (res != FR_OK) fp->obj.fs = 0; /* Invalidate file object on error */ LEAVE_FF(fs, res); }

(1)在注释1的地方,先调用INIT_NAMBUF(fs)宏,初始化fs->lfn,这个lfn之后会被用来存放path中以’/'为分隔符的各个段的名字。 (2)接着在注释2的地方调用follow_path(&dj, path),追踪path的各个段,下面是follow_path()函数的源码:

/*-----------------------------------------------------------------------*/ /* 跟踪文件路径 */ /*-----------------------------------------------------------------------*/ static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ DIR* dp, /* Directory object to return last directory and found object */ const TCHAR* path /* Full-path string to find a file or directory */ ) { FRESULT res; BYTE ns; _FDID *obj = &dp->obj; FATFS *fs = obj->fs; { /* With heading separator */ while (*path == '/' || *path == '\\') path++; /* Strip heading separator */ obj->sclust = 0; /* Start from the root directory */ } if ((UINT)*path /* Follow path */ for (;;) { /***************** 1.获取path的每一段名字 *****************/ res = create_name(dp, &path); if (res != FR_OK) break; /***************** 2.判断磁盘上面是否存在提取出来的目录项 *****************/ res = dir_find(dp); /* Find an object with the segment name */ /***************** 3.获取当前文件名获取的状态 *****************/ ns = dp->fn[NSFLAG]; if (res != FR_OK) { /* Failed to find the object */ if (res == FR_NO_FILE) { /* 不存在此目录项 */ if (_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exist, stay there */ if (!(ns & NS_LAST)) continue; /* Continue to follow if not last segment */ dp->fn[NSFLAG] = NS_NONAME; res = FR_OK; } else { /* Could not find the object */ if (!(ns & NS_LAST)) res = FR_NO_PATH; /* Adjust error code if not last segment */ } } break; } if (ns & NS_LAST) break; /* 是最后一段 */ /* Get into the sub-directory */ if (!(obj->attr & AM_DIR)) { /* 不是子目录,不需要再追踪 */ res = FR_NO_PATH; break; } { /********* 4.设置下一个簇的位置*********/ obj->sclust = ld_clust(fs, fs->win + dp->dptr % SS(fs)); /* Open next directory */ } } } return res; }

在这个函数中又调用create_name()和dir_find(dp)两个函数,这两个函数是追踪的核心代码,也是f_open的核心代码。 (2.1)下面是create_name()的源码:

/*-----------------------------------------------------------------------*/ /* 使用传入的Path,然后用'/'进行分段,提取每一段的名字 ,同时修改path指向的位置*/ /*-----------------------------------------------------------------------*/ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ DIR* dp, /* Pointer to the directory object */ const TCHAR** path /* Pointer to pointer to the segment in the path string */ ) { //#if _USE_LFN != 0 /* LFN configuration */ BYTE b, cf; WCHAR w, *lfn; UINT i, ni, si, di; const TCHAR *p; /* Create LFN in Unicode */ p = *path; lfn = dp->obj.fs->lfnbuf; si = di = 0; for (;;) { w = p[si++]; /* 从path中获取一个字符 */ if (w /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ b = (BYTE)p[si++]; /* Get 2nd byte */ w = (w


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3